package org.marlin.http.http;

import org.marlin.http.conf.HttpConfigProperties;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustAllStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.concurrent.TimeUnit;


@Component
/**
 * init version 1.5.0
 * @author Jack ZG
 * @since 1.0.0
 */
public class HttpConnectionManager {
    private static final Logger log = LoggerFactory.getLogger(HttpConnectionManager.class);
    private static final Integer DEF_CONNECT_TIME_TOLIVE = Integer.valueOf(600000);
    private static final Integer DEF_MAX_TOTAL = Integer.valueOf(2000);
    private static final Integer DEF_MAX_PER_ROUTE = Integer.valueOf(200);
    private static final Integer DEF_VALIDATE_AFTER_INACTIVITY = Integer.valueOf(2000);
    private static final Integer DEF_CONNECTION_REQUEST_TIMEOUT = Integer.valueOf(30000);
    private static final Integer DEF_CONNECTION_TIMEOUT = Integer.valueOf(15000);
    private static final Integer DEF_SOCKET_TIMEOUT = Integer.valueOf(120000);

    private static volatile PoolingHttpClientConnectionManager cm;

    private static volatile CloseableHttpClient httpClient;

    @Autowired
    private HttpConfigProperties httpConfigProperties;


    @PostConstruct
    public static void init() {
        try {
            httpClient = initHttpClientByCm();
        } catch (GeneralSecurityException e) {
            log.error("初始化httpClient出错！", e);
        }
    }

    @PreDestroy
    public void destory() {
        try {
            httpClient.close();
        } catch (IOException e) {
            log.error("关闭httpClient出错！", e);
        }
    }

    public static synchronized CloseableHttpClient getHttpClient() {
        if (httpClient == null) {
            init();
        }
        return httpClient;
    }

    public static PoolingHttpClientConnectionManager getConnectionManager() {
        return cm;
    }


    private static CloseableHttpClient initHttpClientByCm() throws GeneralSecurityException {
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.
                <ConnectionSocketFactory>create().register("http", PlainConnectionSocketFactory.INSTANCE).register("https", getSSLConnectionSocketFactory()).build();

        int connectTimeToLive = DEF_CONNECT_TIME_TOLIVE.intValue();
        cm = new PoolingHttpClientConnectionManager(socketFactoryRegistry, null, null, null, connectTimeToLive, TimeUnit.MILLISECONDS);
        cm.setMaxTotal(DEF_MAX_TOTAL.intValue());
        cm.setDefaultMaxPerRoute(DEF_MAX_PER_ROUTE.intValue());
        cm.setValidateAfterInactivity(DEF_VALIDATE_AFTER_INACTIVITY.intValue());

        RequestConfig rc = RequestConfig.custom().setConnectionRequestTimeout(DEF_CONNECTION_REQUEST_TIMEOUT.intValue()).setConnectTimeout(DEF_CONNECTION_TIMEOUT.intValue()).setSocketTimeout(DEF_SOCKET_TIMEOUT.intValue()).build();
        return HttpClients.custom()
                .setConnectionManager((HttpClientConnectionManager) cm)
                .setDefaultRequestConfig(rc)
                .build();
    }

    private static SSLConnectionSocketFactory getSSLConnectionSocketFactory() throws GeneralSecurityException {
        SSLContext sslContext = initSSLContext();
        SSLConnectionSocketFactory sf = new SSLConnectionSocketFactory(sslContext, new String[]{"TLSv1", "TLSv1.1", "TLSv1.2"}, null, (HostnameVerifier) NoopHostnameVerifier.INSTANCE);


        return sf;
    }

    private static SSLContext initSSLContext() throws GeneralSecurityException {
        SSLContextBuilder builder = new SSLContextBuilder();
        builder.loadTrustMaterial((TrustStrategy) TrustAllStrategy.INSTANCE);
        return builder.build();
    }
}

